#pragma once
#include <vector>
#include <unordered_map>
#include <functional>
#include <sys/select.h>
#include "Socket.hpp"

inline void PrintFdSet(fd_set* fds, int max_fd)
{
    printf("select fds: ");
    for(int i = 0; i < max_fd + 1; i++)
    {
        if(!FD_ISSET(i, fds))
        {
            continue;
        }
        printf("%d ",i);
    }
    printf("\n");
}

using Handler = std::function<void (const std::string& req, std::string* resp)>;
class Selector
{
    using Socket = SocketModule::Socket;
    using TcpSocket = SocketModule::TcpSocket;
public:
    Selector()
    {
        max_fd_ = 0;
        FD_ZERO(&read_fds_);
    }

    bool Add(const TcpSocket& sock)
    {
        int fd = sock.GetSocket();
        printf("[Slector::Add] %d\n", fd);
        if(fd_map_.find(fd) != fd_map_.end())
        {
            printf("Add failed! fd has in Selector!\n");
            return false;
        }
        fd_map_[fd] = sock;
        FD_SET(fd, &read_fds_);
        if(fd > max_fd_)
        {
            max_fd_ = fd;
        }
        return true;
    }

    bool Del(const TcpSocket& sock)
    {
        //1、从位图中清零
        int fd = sock.GetSocket();
        printf("[Selector::Del] %d\n", fd);
        if(FD_ISSET(fd, &read_fds_))
        {
            FD_CLR(fd, &read_fds_);
        }
        //2、如果修改的是最大的，那么要修改最大文件描述符值
        for(int i = max_fd_ ; i >= 0; i--)
        {
            if(!FD_ISSET(i, &read_fds_))
            {
                continue;
            }
            max_fd_ = i;
            break;
        }
        //3、从映射表中删除映射
        if(fd_map_.count(fd)) fd_map_.erase(fd);
        return true;
    }

    bool Wait(std::vector<TcpSocket> *output)
    {
        output->clear();
        fd_set tmp = read_fds_;
        PrintFdSet(&tmp, max_fd_);
        printf(">");
        fflush(stdout);
        int nfds = select(max_fd_ + 1, &tmp, nullptr, nullptr, nullptr);
        if(nfds < 0)
        {
            perror("select");
            return false;
        }
        for(int i = 0; i < max_fd_ + 1; i++)
        {
            if(!FD_ISSET(i, &tmp))
            {
                continue;
            }
            output->push_back(fd_map_[i]);
        }
        return true;
    }
private:
    fd_set read_fds_;
    int max_fd_;
    std::unordered_map<int, TcpSocket> fd_map_;
};